package com.mornd.gulimall.order.service.impl;

import cn.hutool.core.collection.CollUtil;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.conditions.update.LambdaUpdateWrapper;
import com.baomidou.mybatisplus.core.toolkit.Wrappers;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.mornd.gulimall.apis.cart.ICartClient;
import com.mornd.gulimall.apis.cart.to.CartItemTo;
import com.mornd.gulimall.apis.member.IMemberClient;
import com.mornd.gulimall.apis.member.to.MemberReceiveAddressTo;
import com.mornd.gulimall.apis.order.to.OrderItemTo;
import com.mornd.gulimall.apis.order.to.OrderPageTo;
import com.mornd.gulimall.apis.order.to.OrderTo;
import com.mornd.gulimall.apis.product.IProductClient;
import com.mornd.gulimall.apis.product.to.SkuInfoTo;
import com.mornd.gulimall.apis.product.to.SpuInfoTo;
import com.mornd.gulimall.apis.seckill.to.SeckillOrderTo;
import com.mornd.gulimall.apis.ware.IWareClient;
import com.mornd.gulimall.apis.ware.to.OrderLockStockResult;
import com.mornd.gulimall.apis.ware.to.WareSkuLockTo;
import com.mornd.gulimall.common.core.auth.UserHolder;
import com.mornd.gulimall.common.core.entity.SessionUser;
import com.mornd.gulimall.common.core.exception.ServiceException;
import com.mornd.gulimall.common.core.result.R;
import com.mornd.gulimall.common.core.utils.BigDecimals;
import com.mornd.gulimall.order.config.pay.AliPay;
import com.mornd.gulimall.order.config.pay.AliPayAsyncVo;
import com.mornd.gulimall.order.constant.OrderConstant;
import com.mornd.gulimall.common.core.constant.OrderStatus;
import com.mornd.gulimall.order.entity.Order;
import com.mornd.gulimall.order.entity.OrderItem;
import com.mornd.gulimall.order.entity.PaymentInfo;
import com.mornd.gulimall.order.entity.vo.OrderConfirmVO;
import com.mornd.gulimall.order.entity.vo.OrderSubmitVo;
import com.mornd.gulimall.order.mapper.OrderMapper;
import com.mornd.gulimall.order.service.IOrderItemService;
import com.mornd.gulimall.order.service.IOrderService;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.mornd.gulimall.order.service.IPaymentInfoService;
import com.mornd.gulimall.order.utils.IdWorker;
//import io.seata.spring.annotation.GlobalTransactional;
import lombok.extern.slf4j.Slf4j;
import org.springframework.amqp.rabbit.core.RabbitTemplate;
import org.springframework.beans.BeanUtils;
import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.data.redis.core.script.DefaultRedisScript;
import org.springframework.scheduling.concurrent.ThreadPoolTaskExecutor;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.web.context.request.RequestAttributes;
import org.springframework.web.context.request.RequestContextHolder;

import javax.annotation.Resource;
import java.math.BigDecimal;
import java.time.LocalDateTime;
import java.time.format.DateTimeFormatter;
import java.util.Collections;
import java.util.List;
import java.util.Map;
import java.util.concurrent.CompletableFuture;
import java.util.concurrent.TimeUnit;
import java.util.stream.Collectors;

/**
 * <p>
 * 订单 服务实现类
 * </p>
 *
 * @author mornd
 * @since 2023-07-22
 */

@Slf4j
@Service
public class OrderServiceImpl extends ServiceImpl<OrderMapper, Order> implements IOrderService {
    @Resource
    private IMemberClient memberClient;
    @Resource
    private ICartClient cartClient;
    @Resource
    private IProductClient productClient;
    @Resource
    private StringRedisTemplate stringRedisTemplate;
    @Resource
    private ThreadPoolTaskExecutor threadPoolTaskExecutor;
    @Resource
    private IOrderItemService orderItemService;
    @Resource
    private IWareClient wareClient;
    @Resource
    private RabbitTemplate rabbitTemplate;
    @Resource
    private IPaymentInfoService paymentInfoService;

    private static final ThreadLocal<OrderSubmitVo> orderSubmit_TL = new ThreadLocal<>();

    @Override
    public OrderConfirmVO confirmOrder(Map<String, String> params) {
        OrderConfirmVO orderConfirmVO = new OrderConfirmVO();

        SessionUser user = UserHolder.getUser();

        RequestAttributes requestAttributes = RequestContextHolder.getRequestAttributes();

        CompletableFuture<Void> c1 = CompletableFuture.runAsync(() -> {
            // 收货地址
            try {
                RequestContextHolder.setRequestAttributes(requestAttributes);
                R<List<MemberReceiveAddressTo>> r = memberClient.getAddressByMemberId(user.getId());
                if (r.isFail()) {
                    throw new ServiceException(r.getMsg());
                }
                orderConfirmVO.setAddress(r.getData());
            } catch (Exception e) {
                log.error(e.getMessage());
//                throw new ServiceException(e.getMessage());
            } finally {
                RequestContextHolder.resetRequestAttributes();
            }
        }, threadPoolTaskExecutor);

        CompletableFuture<Void> c2 = CompletableFuture.runAsync(() -> {
            // 购物项
            try {
                RequestContextHolder.setRequestAttributes(requestAttributes);
                R<List<CartItemTo>> r = cartClient.getCartItems(user.getId());
                if (r.isFail()) {
                    throw new ServiceException(r.getMsg());
                }
                List<CartItemTo> items = r.getData().stream().filter(i -> {
                    if (params.containsKey(i.getSkuId().toString())) {
                        i.setCount(Integer.valueOf(params.get(i.getSkuId().toString())));
                        return true;
                    } else {
                        return false;
                    }
                }).collect(Collectors.toList());
                orderConfirmVO.setItems(items);
            } catch (Exception e) {
                log.error(e.getMessage());
                //throw new ServiceException(e.getMessage());
            } finally {
                RequestContextHolder.resetRequestAttributes();
            }
        }, threadPoolTaskExecutor);

        CompletableFuture.allOf(c1, c2).join();

        // 用户积分
        orderConfirmVO.setIntegeration(user.getIntegration());

        String token = IdWorker.uuid();
        stringRedisTemplate.opsForValue().set(OrderConstant.SUBMIT_TOKEN_KEY + user.getId(), token, 5, TimeUnit.MINUTES);
        orderConfirmVO.setOrderToken(token);

        return orderConfirmVO;
    }

    /**
     * 柔性事务 可靠信息+最终一致性
     *
     * @param orderSubmitVo
     * @return
     */
//    @GlobalTransactional // 开启分布式事务
    @Transactional(rollbackFor = Exception.class)
    @Override
    public Order submitOrder(OrderSubmitVo orderSubmitVo) {
        orderSubmit_TL.set(orderSubmitVo);

        try {
            String key = OrderConstant.SUBMIT_TOKEN_KEY + UserHolder.getUser().getId();

            String script = "if redis.call('get', KEYS[1]) == ARGV[1] then return redis.call('del',KEYS[1]) else return 0 end";

            Long result = stringRedisTemplate.execute(new DefaultRedisScript<>(script, Long.class), Collections.singletonList(key), orderSubmitVo.getOrderToken());

            if (result != null && result == 1) {
                // 验证成功
                return createOrder();
            } else {
                throw new ServiceException("不允许重复提交");
            }
        } finally {
            orderSubmit_TL.remove();
        }
    }

    /**
     * 创建订单
     *
     * @return
     */
    private Order createOrder() {
        OrderSubmitVo orderSubmitVo = orderSubmit_TL.get();

        Order order = new Order();
        order.setMemberId(UserHolder.getUser().getId());
        order.setMemberUsername(UserHolder.getUser().getUsername());
        String timeId = com.baomidou.mybatisplus.core.toolkit.IdWorker.getTimeId();
        order.setOrderSn(timeId);
        // 获取收货地址信息
        R<MemberReceiveAddressTo> r = memberClient.getAddressById(orderSubmitVo.getAddrId());
        if (r.isFail()) {
            log.error(r.getMsg());
            throw new ServiceException(r.getMsg());
        }
        MemberReceiveAddressTo addressTo = r.getData();
        // 收货信息
        order.setReceiverProvince(addressTo.getProvince());
        order.setReceiverCity(addressTo.getCity());
        order.setReceiverRegion(addressTo.getRegion());
        order.setReceiverDetailAddress(addressTo.getDetailAddress());
        order.setReceiverName(addressTo.getName());
        order.setReceiverPhone(addressTo.getPhone());
        order.setReceiverPostCode(addressTo.getPostCode());

        List<OrderItem> orderItems = buildOrderItems(order.getOrderSn());

        calcPrice(order, orderItems);
        order.setStatus(OrderStatus.CREATE_NEW.getCode());
        order.setAutoConfirmDay(7);
        order.setDeleteStatus(0);
        order.setPayType(orderSubmitVo.getPayType());
        order.setNote(orderSubmitVo.getNote());
        order.setCreateTime(LocalDateTime.now());

        super.save(order);

        orderItemService.saveBatch(orderItems);

        // 库存锁定
        WareSkuLockTo wareSkuLockTo = new WareSkuLockTo();
        wareSkuLockTo.setOrderSn(order.getOrderSn());
        List<WareSkuLockTo.SkuInfo> lockSkuInfo = orderItems.stream().map(i -> {
            WareSkuLockTo.SkuInfo orderLockTo = new WareSkuLockTo.SkuInfo();
            orderLockTo.setSkuId(i.getSkuId());
            orderLockTo.setCount(i.getSkuQuantity());
            return orderLockTo;
        }).collect(Collectors.toList());
        wareSkuLockTo.setSkuInfo(lockSkuInfo);
        R<OrderLockStockResult> lockStockR = wareClient.orderLockStock(wareSkuLockTo);
        if (lockStockR.isFail()) {
            throw new ServiceException("库存锁定失败");
        }
        if (!lockStockR.getData().isLocked()) {
            throw new ServiceException("库存锁定失败，" + lockStockR.getData().getFailMsg());
        }
//        int a = 1 / 0;

        // 给mq发送订单消息
        rabbitTemplate.convertAndSend("order-event-exchange",
                "order.create.order",
                order);

        return order;
    }

    /**
     * 构建订单项
     *
     * @param orderSn
     * @return
     */
    private List<OrderItem> buildOrderItems(String orderSn) {
        OrderSubmitVo orderSubmitVo = orderSubmit_TL.get();
        return orderSubmitVo.getSkus().stream().map(it -> {
            OrderItem orderItem = new OrderItem();
            orderItem.setOrderSn(orderSn);

            R<SkuInfoTo> skuInfoR = productClient.getSkuInfoById(it.getSkuId());
            if (skuInfoR.isFail()) {
                log.error(skuInfoR.getMsg());
                throw new ServiceException(skuInfoR.getMsg());
            }
            SkuInfoTo skuInfo = skuInfoR.getData();

            R<SpuInfoTo> spuInR = productClient.getSpuInfo(skuInfo.getSpuId());
            if (spuInR.isFail()) {
                log.error(spuInR.getMsg());
                throw new ServiceException(spuInR.getMsg());
            }
            SpuInfoTo spuInfo = spuInR.getData();

            R<List<String>> attrsR = productClient.getSkuSaleStringlist(it.getSkuId());
            if (attrsR.isFail()) {
                log.error(attrsR.getMsg());
                throw new ServiceException(attrsR.getMsg());
            }

            List<String> attrs = attrsR.getData();

            // sku
            orderItem.setSkuId(it.getSkuId());
            orderItem.setSkuQuantity(it.getCount());
            orderItem.setSkuName(skuInfo.getSkuName());
            orderItem.setSkuPic(skuInfo.getSkuDefaultImg());
            orderItem.setSkuPrice(skuInfo.getPrice());
            if (CollUtil.isNotEmpty(attrs)) {
                orderItem.setSkuAttrsVals(String.join(",", attrs));
            }

            // spu
            orderItem.setSpuId(skuInfo.getSpuId());
            orderItem.setSpuName(spuInfo.getSpuName());
            orderItem.setCategoryId(spuInfo.getCatalogId());
            orderItem.setSpuBrand(spuInfo.getBrandId().toString());

            // 优惠
            orderItem.setPromotionAmount(BigDecimal.ZERO);
            orderItem.setCouponAmount(BigDecimal.ZERO);
            orderItem.setIntegrationAmount(BigDecimal.ZERO);

            // 优惠后价格
            orderItem.setRealAmount(
                    orderItem.getSkuPrice().multiply(new BigDecimal(orderItem.getSkuQuantity().toString()))
                            .subtract(orderItem.getPromotionAmount())
                            .subtract(orderItem.getCouponAmount())
                            .subtract(orderItem.getIntegrationAmount())
            );


            // todo 成长积分
            orderItem.setGiftGrowth(skuInfo.getPrice().multiply(new BigDecimal(it.getCount())).intValue());
            orderItem.setGiftIntegration(skuInfo.getPrice().multiply(new BigDecimal(it.getCount())).intValue());
            return orderItem;
        }).collect(Collectors.toList());
    }

    /**
     * 计算订单价格
     *
     * @return
     */
    private void calcPrice(Order order, List<OrderItem> items) {
        // todo 获取邮费
        order.setFreightAmount(BigDecimal.ONE);
        order.setDiscountAmount(BigDecimal.ZERO);

        BigDecimal totalPrice = items.stream()
                .map(OrderItem::getRealAmount)
                .reduce(BigDecimal.ZERO, BigDecimal::add);

        // 订单总额
        order.setTotalAmount(totalPrice);
        // 应付金额 = 订单总额+运费金额-
        order.setPayAmount(totalPrice.add(order.getFreightAmount()));

        // 订单优惠金额
        order.setCouponAmount(items.stream()
                .map(OrderItem::getCouponAmount)
                .reduce(BigDecimal.ZERO, BigDecimal::add));
        order.setPromotionAmount(items.stream()
                .map(OrderItem::getPromotionAmount)
                .reduce(BigDecimal.ZERO, BigDecimal::add));
        order.setIntegrationAmount(items.stream()
                .map(OrderItem::getIntegrationAmount)
                .reduce(BigDecimal.ZERO, BigDecimal::add));

        // 获得的总成长值
        order.setGrowth(items.stream()
                .map(OrderItem::getGiftGrowth)
                .reduce(0, Integer::sum));

        // 获得积分值
        order.setIntegration(items.stream()
                .map(OrderItem::getGiftIntegration)
                .reduce(0, Integer::sum));
    }

    /**
     * 关单
     *
     * @param order
     */
    @Override
    public void closeOrder(Order order) {
        Order dbOrder = super.getById(order.getId());
        if (dbOrder != null && dbOrder.getStatus() == OrderStatus.CREATE_NEW.getCode()) {
            Order wrapper = new Order();
            wrapper.setId(order.getId());
            wrapper.setStatus(OrderStatus.CANCLED.getCode());
            wrapper.setModifyTime(LocalDateTime.now());
            super.updateById(wrapper);

            OrderTo orderTo = new OrderTo();
            BeanUtils.copyProperties(dbOrder, orderTo);
            orderTo.setStatus(wrapper.getStatus());
            // 主动再一次通知库存服务解锁订单

            // 发送失败的消息保存到数据库，定期扫描发送失败的消息重新投递
            rabbitTemplate.convertAndSend("order-event-exchange",
                    "order.release.other",
                    orderTo);
        }
    }

    /**
     * 生成订单支付信息
     *
     * @param orderSn
     * @return
     */
    @Override
    public AliPay getPayInfo(String orderSn) {
        Order order = super.getOne(Wrappers.<Order>lambdaQuery().eq(Order::getOrderSn, orderSn));
        if (order == null) {
            return null;
        }
        AliPay aliPay = new AliPay();
        aliPay.setOut_trade_no(order.getOrderSn());
        // 只接受小数点后俩位
        aliPay.setTotal_amount(BigDecimals.setScale(order.getPayAmount()));
        aliPay.setSubject(LocalDateTime.now().toString() + "订单");

        Order db = new Order();
        db.setId(order.getId());
        db.setPaymentTime(LocalDateTime.now());
        super.updateById(db);
        return aliPay;
    }

    @Override
    public OrderPageTo pageWithItemByUser(OrderPageTo orderPageTo) {
        Page<Order> page = new Page<>(orderPageTo.getPageNo(), orderPageTo.getPageSize());
        Long memberId = UserHolder.getUser().getId();
        LambdaQueryWrapper<Order> qw = Wrappers.<Order>lambdaQuery().eq(Order::getMemberId, memberId);
        qw.orderByDesc(Order::getCreateTime);
        super.page(page, qw);

        List<OrderTo> orders = page.getRecords().stream().map(it -> {
            OrderTo orderTo = new OrderTo();
            BeanUtils.copyProperties(it, orderTo);

            // 查询订单对应的订单项
            List<OrderItem> items = orderItemService.list(Wrappers.<OrderItem>lambdaQuery().eq(OrderItem::getOrderSn, it.getOrderSn()));
            List<OrderItemTo> itemTos = items.stream().map(it2 -> {
                OrderItemTo itemTo = new OrderItemTo();
                BeanUtils.copyProperties(it2, itemTo);
                return itemTo;
            }).collect(Collectors.toList());

            orderTo.setItems(itemTos);
            return orderTo;
        }).collect(Collectors.toList());

        orderPageTo.setOrderList(orders);
        orderPageTo.setPageSize(page.getPages());
        orderPageTo.setTotal(page.getTotal());
        return orderPageTo;
    }

    /**
     * 处理支付成功后，平台的异步回调通知请求
     *
     * @param vo
     * @return
     */
    @Override
    @Transactional(rollbackFor = Exception.class)
    public void handlePayedResult(AliPayAsyncVo vo) {
        // 本系统订单号
        String out_trade_no = vo.getOut_trade_no();
        // 支付宝给的本次交易流水号
        String trade_no = vo.getTrade_no();

        // 保存交易流水
        PaymentInfo paymentInfo = new PaymentInfo();
        DateTimeFormatter dtf = DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss");
        paymentInfo.setOrderSn(out_trade_no);
        paymentInfo.setAlipayTradeNo(trade_no);
        paymentInfo.setSubject(vo.getSubject());
        paymentInfo.setPaymentStatus(vo.getTrade_status());
        paymentInfo.setTotalAmount(new BigDecimal(vo.getTotal_amount()));
        paymentInfo.setCallbackTime(LocalDateTime.parse(vo.getNotify_time(), dtf));
        paymentInfo.setCreateTime(LocalDateTime.parse(vo.getGmt_create(), dtf));
        paymentInfoService.save(paymentInfo);

        // 可退款情况下的交易成功
        final String trade_success = "TRADE_SUCCESS";
        // 不可退款情况下的交易成功
        final String trade_finished = "TRADE_FINISHED";

        if (trade_success.equals(vo.getTrade_status())
                || trade_finished.equals(vo.getTrade_status())) {
            // 更改订单状态
            LambdaUpdateWrapper<Order> uw = Wrappers.<Order>lambdaUpdate();
            uw.set(Order::getStatus, OrderStatus.PAYED.getCode());
            uw.set(Order::getModifyTime, LocalDateTime.now());
            uw.set(Order::getPaymentTime, LocalDateTime.parse(vo.getGmt_payment(), dtf));

            uw.eq(Order::getOrderSn, out_trade_no);
            super.update(uw);
        }

    }

    /**
     * 处理秒杀单
     *
     * @param seckillOrderTo
     */
    @Override
    @Transactional(rollbackFor = Exception.class)
    public void createSeckillOrder(SeckillOrderTo seckillOrderTo) {
        // todo 完善秒杀订单创建
        // 保存订单
        Order order = new Order();
        order.setOrderSn(seckillOrderTo.getOrderSn());
        order.setMemberId(seckillOrderTo.getMemberId());
        BigDecimal payAmount = seckillOrderTo.getSeckillPrice().multiply(new BigDecimal(seckillOrderTo.getNum().toString()));
        order.setPayAmount(payAmount);
        order.setStatus(OrderStatus.CREATE_NEW.getCode());
        order.setCreateTime(LocalDateTime.now());
        super.save(order);

        // 保存订单项
        OrderItem item = new OrderItem();
        item.setOrderSn(seckillOrderTo.getOrderSn());
        item.setRealAmount(payAmount);
        item.setSkuQuantity(seckillOrderTo.getNum());
        // todo 获取sku详细信息
        orderItemService.save(item);
    }
}
